package com.vinfai.common.dao.impl;

import java.io.Serializable;

import java.lang.reflect.ParameterizedType;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.hibernate.type.Type;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.object.SqlQuery;

import com.vinfai.common.dao.IEntityDAO;
import com.vinfai.common.exception.DataAccessException;
import com.vinfai.utils.reflection.ReflectionUtils;

@SuppressWarnings("unchecked")
public class HibernateBaseDAOImpl<E,PK extends Serializable> implements IEntityDAO<E, PK> {
	
	protected static Logger logger = Logger.getLogger(HibernateBaseDAOImpl.class); 
	
	@Autowired
	@Qualifier("sessionFactory") //出现多个SessionFactory时区分
	public SessionFactory sessionFactory;
	
	public Class<E> entityClazz;

	/**
	 * 1.在实际调用时，需要从父类中获得泛型定义中的实际类型。
	 * eg：UserDAO extends HibernateBaseDAOImpl<User, java.lang.Long> ==> 在new UserDAO时，
	 * 会根据参数<user,Long>中获得实际的类型为User.class
	 */
	public HibernateBaseDAOImpl(){
		this.entityClazz = ReflectionUtils.getSuperClassGenricType(getClass());
	}
	
	public HibernateBaseDAOImpl(SessionFactory sessionFactory,Class<E> entityClass){
		this.sessionFactory = sessionFactory;
		this.entityClazz = entityClass;
	}
	
	public Session getSession(){
		//return this.getSessionFactory().openSession();
		return sessionFactory.getCurrentSession();
	}
	
	public void delete(E entity){
		getSession().delete(entity);
	}

	public void deleteById(PK id)  {
		getSession().delete(this.getById(id));
	}

	public E getById(PK id) {
		return (E)getSession().get(this.entityClazz, id);
	}

	public void merge(E entity) {
		getSession().merge(entity);
	}

	public PK save(E entity) {
		return (PK)getSession().save(entity);
	}

	public void saveOrUpdate(E entity){
		getSession().saveOrUpdate(entity);
	}

	public void update(E entity){
		getSession().update(entity);
	}
	/*
	 * (non-Javadoc)
	 * @see com.vinfai.common.dao.IEntityDAO#bulkUpdate(java.lang.String, java.util.Map)
	 */
	public int bulkUpdate(String updateSql, Map<String, ?> params)
		throws DataAccessException {
		Query query  = getSession().createQuery(updateSql);
		//设置参数
		this.applyParameters(params, query);
		return query.executeUpdate();
	}
	
	public int bulkUpdate(String updateSql) throws DataAccessException{
		try {
			return getSession().createQuery(updateSql).executeUpdate();
		} catch (Exception e) {
			throw new DataAccessException("bulk update error:"+updateSql, e);
		}
	}
	
	
	public int bulkNativeUpdate(String nativeSql) throws DataAccessException {
		try {
			SQLQuery query = getSession().createSQLQuery(nativeSql);
			return query.executeUpdate();
		} catch (Exception e) {
			throw new DataAccessException("bulk update error:"+nativeSql, e);
		}
	}

	public int bulkNativeUpdate(String nativeSql, Map<String, ?> params)
			throws DataAccessException {
		try {
			SQLQuery query = getSession().createSQLQuery(nativeSql);
			this.applyParameters(params, query);
			return query.executeUpdate();
		} catch (Exception e) {
			throw new DataAccessException("bulk update error:"+nativeSql, e);
		}
	}

	public int bulkNativeUpdate(String nativeSql, Object... params)
			throws DataAccessException {
		try {
			SQLQuery query = getSession().createSQLQuery(nativeSql);
			this.applyParameters(params, query);
			return query.executeUpdate();
		} catch (Exception e) {
			throw new DataAccessException("bulk update error:"+nativeSql, e);
		}
	}

	public int bulkUpdate(String updateSql, Object... params)
			throws DataAccessException {
		try {
			Query query = getSession().createQuery(updateSql);
			this.applyParameters(params, query);
			return query.executeUpdate();
		} catch (Exception e) {
			throw new DataAccessException("bulk update error:"+updateSql, e);
		}
	}

	public <T> List<T> find(String hql, Map<String, ?> params)
			throws DataAccessException {
		return find(hql,-1,-1,params);
	}

	public <T> List<T> find(String hql, Object... params)
			throws DataAccessException {
		return find(hql, -1,-1,params);
	}

	public <T> List<T> find(String hql, int firstResult, int maxResults,
			Map<String, ?> params) throws DataAccessException {
		try{
			Query query = getSession().createQuery(hql);
			this.applyParameters(params, query);
			if(firstResult>0){
				query.setFirstResult(firstResult);
			}
			//-1: 表示不限制
			if(maxResults>0){
				query.setMaxResults(maxResults);
			}
			List<T> result = query.list();
			return result;
		}catch(Exception e){
			throw new DataAccessException("find list error.", e);
		}
	}

	public <T> List<T> find(String hql, int firstResult, int maxResults,
			Object... params) throws DataAccessException {
		try{
			Query query = getSession().createQuery(hql);
			this.applyParameters(params, query);
			if(firstResult>0){
				query.setFirstResult(firstResult);
			}
			//-1: 表示不限制
			if(maxResults>0){
				query.setMaxResults(maxResults);
			}
			List<T> result = query.list();
			return result;
		}catch(Exception e){
			throw new DataAccessException("find list error.", e);
		}
	}
	
	public <T> T findUniqueByNativeSQL(String nativeSql, Map<String, ?> params)
		throws DataAccessException {
		try {
			Query query = getSession().createSQLQuery(nativeSql);
			this.applyParameters(params, query);
			return (T)query.uniqueResult();
		} catch (Exception e) {
			throw new DataAccessException("find unique error:"+nativeSql, e);
		}
	}
	
	public <T> T findUniqueByNativeSQL(String nativeSql, Object... params)
		throws DataAccessException {
		try {
			Query query = getSession().createSQLQuery(nativeSql);
			this.applyParameters(params, query);
			return (T)query.uniqueResult();
		} catch (Exception e) {
			throw new DataAccessException("find unique error:"+nativeSql, e);
		}
	}

	public <T> T findUnique(String hql, Object... params)
		throws DataAccessException {
		try {
			Query query = getSession().createQuery(hql);
			this.applyParameters(params, query);
			Object obj = query.uniqueResult();
			System.out.println(obj.getClass().getName());
			return (T)query.uniqueResult();
		} catch (Exception e) {
			throw new DataAccessException("find unique error:"+hql, e);
		}
	}
	
	public <T> T findUnique(String hql, Map<String, ?> params)
		throws DataAccessException {
		try {
			Query query = getSession().createQuery(hql);
			this.applyParameters(params, query);
			return (T)query.uniqueResult();
		} catch (Exception e) {
			throw new DataAccessException("find unique error:"+hql, e);
		}
	}
	
	public <T> T countSQLResult(final String nativeSql,final Map<String,?> params,final Map<String,Type> scalarMap){
		//Map<String,Type> replaced final List<Entry<String, Type>> 
		try {
			SQLQuery query = getSession().createSQLQuery(nativeSql);
			/*if (scalarList != null) {
	            for (Entry<String, Type> entity : scalarList) {
	            	//KEY :必须都是大写，且同SQL中相同??好像不一定要大写
	                query.addScalar(entity.getKey(), entity.getValue());
	            }
	        }*/
			if(scalarMap!=null){
				Iterator<Entry<String, Type>> iter = scalarMap.entrySet().iterator();
				while(iter.hasNext()){
					  Entry<String, Type> entry = iter.next();
					  query.addScalar(entry.getKey(), entry.getValue());
				}
			}
			this.applyParameters(params, query);
			return (T)query.uniqueResult();
		} catch (Exception e) {
			throw new DataAccessException("find unique error:"+nativeSql, e);
		}
	}
	
	protected Query applyParameters(Map<String,?> params,Query query){
		if(params==null||params.keySet().size()==0){
			logger.debug("param map is empty,can't apply query parameter from map.");
			return query;
		}
		//设置MAP参数
		query.setProperties(params);
		return query;
	}
	/**
	 * query对象参数设置，按顺序绑定
	 * @param params
	 * @param query
	 * @return
	 */
	protected Query applyParameters(Object[] params,Query query){
		if(params==null||params.length==0){
			logger.debug("param map is empty,can't apply query parameter from map.");
			return query;
		}
		for(int i=0;i<params.length;i++){
			query.setParameter(i, params[i]);
		}
		return query;
	}
	


	public <T> List<T> findNativeQuery(String nativeSql, int firstResult,
			int maxResults, 
			Object... params) throws DataAccessException {
		try{
			SQLQuery query = getSession().createSQLQuery(nativeSql);
			//System.out.println("query:"+query);
			/*if(scalarList!=null&&scalarList.size()>0){
				for(Entry<String, Type> entry : scalarList){
					query.addScalar(entry.getKey(), entry.getValue());
				}
			}*/
			this.applyParameters(params, query);
			//设置Transformer属性，则可将放回的结果的每一行放到MAP中(List<Map>),KEY:为DB中名称一致，或者别名
			//query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
			//setResultTransformer(Transformers.aliasToBean(Person.class)));  
			return query.list();
		}catch(Exception e){
			throw new DataAccessException("find by niative SQL error:"+nativeSql, e);
		}
	}

	public <T> List<T> findNativeQuery(String nativeSql, int firstResult,
			int maxResults,
			Map<String, ?> params) throws DataAccessException {
		try{
			SQLQuery query = getSession().createSQLQuery(nativeSql);
			/*if(scalarList!=null&&scalarList.size()>0){
				for(Entry<String, Type> entry : scalarList){
					query.addScalar(entry.getKey(), entry.getValue());
				}
			}*/
			this.applyParameters(params, query);
			return query.list();
		}catch(Exception e){
			throw new DataAccessException("find by niative SQL error:"+nativeSql, e);
		}
	}

	
	/**
	 * 取得sessionFactory.
	 */
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public Class<E> getEntityClazz() {
		return entityClazz;
	}

	public void setEntityClazz(Class<E> entityClazz) {
		this.entityClazz = entityClazz;
	}

	public <T> List<T> findNativeQueryToBean(String nativeSql, int firstResult,
			int maxResults, Class<T> beanClass, Map<String, Type> scalarMap,Object... params)
			throws DataAccessException {
		try{
			SQLQuery query = getSession().createSQLQuery(nativeSql);
			this.applyParameters(params, query);
			if(firstResult>0){
				query.setFirstResult(firstResult);
			}
			if(maxResults>0){
				query.setMaxResults(maxResults);
			}
			if(scalarMap!=null){
				Iterator<Entry<String,Type>> iter = scalarMap.entrySet().iterator();
				while(iter.hasNext()){
					Entry<String, Type> entry = iter.next();
					query.addScalar(entry.getKey(), entry.getValue());
				}
			}
			//转化成BEAN形式
			query.setResultTransformer(Transformers.aliasToBean(beanClass));
			return query.list();
		}catch(Exception e){
			throw new DataAccessException("list query result to bean with sql :"+nativeSql, e);
		}
	}

	public <T> List<T> findNativeQueryToMap(String nativeSql, int firstResult,
			int maxResults, Map<String, Type> scalarMap, Object... params)
			throws DataAccessException {
		try{
			SQLQuery query = getSession().createSQLQuery(nativeSql);
			this.applyParameters(params, query);
			if(firstResult>0){
				query.setFirstResult(firstResult);
			}
			if(maxResults>0){
				query.setMaxResults(maxResults);
			}
			if(scalarMap!=null){
				Iterator<Entry<String,Type>> iter = scalarMap.entrySet().iterator();
				while(iter.hasNext()){
					Entry<String, Type> entry = iter.next();
					query.addScalar(entry.getKey(), entry.getValue());
				}
			}
			//转化成Map形式
			query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
			return query.list();
		}catch(Exception e){
			throw new DataAccessException("list query result to map with sql :"+nativeSql, e);
		}
	}
}
